package com.easygoods.service;

import com.bestvike.linq.Linq;
import com.bestvike.stone.core.collection.KeyedCollectionUtils;
import com.bestvike.stone.core.lang.BeanUtils;
import com.bestvike.stone.core.lang.StringUtils;
import com.bestvike.stone.spring.exception.BusinessException;
import com.bestvike.stone.spring.exception.ErrorCode;
import com.bestvike.stone.spring.page.PageableResponse;
import com.bestvike.stone.spring.service.BaseService;
import com.easygoods.controller.TinyUrlController;
import com.easygoods.dao.GoodsCategoryDao;
import com.easygoods.dao.GoodsSkuDao;
import com.easygoods.dao.GoodsSpuBannerDao;
import com.easygoods.dao.GoodsSpuDao;
import com.easygoods.dao.GoodsSpuImageDao;
import com.easygoods.dao.GoodsSpuSpecDao;
import com.easygoods.entity.GoodsCategory;
import com.easygoods.entity.GoodsSpuBanner;
import com.easygoods.entity.GoodsSpuImage;
import com.easygoods.entity.MemberInfo;
import com.easygoods.entity.StoreInfo;
import com.easygoods.entityex.GoodsSkuEx;
import com.easygoods.entityex.GoodsSkuWithSpecs;
import com.easygoods.entityex.GoodsSpuEx;
import com.easygoods.enums.YNEnum;
import com.easygoods.properties.CommonProperties;
import com.easygoods.request.SkuDetailRequest;
import com.easygoods.request.SpuDetailRequest;
import com.easygoods.request.SpuListRequest;
import com.easygoods.request.SpuSearchRequest;
import com.easygoods.request.SpuShareRequest;
import com.easygoods.response.CategoryResponse;
import com.easygoods.response.SkuDetailResponse;
import com.easygoods.response.SpuDetailPublicResponse;
import com.easygoods.response.SpuDetailResponse;
import com.easygoods.response.SpuDetailResponseBanner;
import com.easygoods.response.SpuDetailResponseImage;
import com.easygoods.response.SpuDetailResponseSpec;
import com.easygoods.response.SpuListResponse;
import com.easygoods.response.SpuMultiShareResponse;
import com.easygoods.response.SpuShareResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.math.MathContext;
import java.text.DecimalFormat;
import java.util.List;

/**
 * Created by 许崇雷 on 2019-01-02.
 */
@Service
public class GoodsService extends BaseService {
    private static final String SPU_SHARE_MACRO_PRICE = "${price}";
    @Autowired
    private GoodsCategoryDao goodsCategoryDao;
    @Autowired
    private GoodsSpuDao goodsSpuDao;
    @Autowired
    private GoodsSpuBannerDao goodsSpuBannerDao;
    @Autowired
    private GoodsSpuImageDao goodsSpuImageDao;
    @Autowired
    private GoodsSpuSpecDao goodsSpuSpecDao;
    @Autowired
    private GoodsSkuDao goodsSkuDao;
    @Autowired
    private ConfigService configService;
    @Autowired
    private MemberService memberService;
    @Autowired
    private StoreService storeService;
    @Autowired
    private PointService pointService;
    @Autowired
    private TinyUrlService tinyUrlService;
    @Autowired
    private CommonProperties commonProperties;

    //获取 spu 分类
    public List<CategoryResponse> getCategoryList() {
        final List<GoodsCategory> goodsCategories = this.goodsCategoryDao.selectAll();
        return BeanUtils.toList(goodsCategories, CategoryResponse.class);
    }

    //获取 spu 列表,根据分类 id
    public PageableResponse<SpuListResponse> getSpuList(SpuListRequest request) {
        MemberInfo memberInfo = this.memberService.getMemberInfo();
        if (memberInfo == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "无效的用户");
        if (memberInfo.getGrade() == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "获取用户等级失败");

        final List<GoodsSpuEx> goodsByCategory = this.goodsSpuDao.findSpuListByCategoryId(request.getCategoryId(), request.toRowBounds());//分页信息
        for (GoodsSpuEx item : goodsByCategory) {
            //计算会员价,可赚
            item.setMemberPrice(this.getMemberPrice(memberInfo.getGrade(), item.getMinPrice(), item.getOriginPrice()));
            item.setEarnPrice(this.getEarnPrice(item.getOriginPrice(), item.getMemberPrice()));
        }
        final List<SpuListResponse> goodsSpuListResponse = BeanUtils.toList(goodsByCategory, SpuListResponse.class);//类型转换
        return PageableResponse.create(goodsByCategory, goodsSpuListResponse);
    }

    //spu 搜索
    public PageableResponse<SpuListResponse> search(SpuSearchRequest request) {
        MemberInfo memberInfo = this.memberService.getMemberInfo();
        if (memberInfo == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "无效的用户");
        if (memberInfo.getGrade() == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "获取用户等级失败");

        final List<GoodsSpuEx> goodsByCategory = this.goodsSpuDao.findSpuListByKeyword(request.getKeyword(), request.toRowBounds());//分页信息
        for (GoodsSpuEx item : goodsByCategory) {
            //计算会员价,可赚
            item.setMemberPrice(this.getMemberPrice(memberInfo.getGrade(), item.getMinPrice(), item.getOriginPrice()));
            item.setEarnPrice(this.getEarnPrice(item.getOriginPrice(), item.getMemberPrice()));
        }
        final List<SpuListResponse> goodsSpuListResponse = BeanUtils.toList(goodsByCategory, SpuListResponse.class);//类型转换
        return PageableResponse.create(goodsByCategory, goodsSpuListResponse);
    }

    //spu 搜索
    public PageableResponse<SpuListResponse> searchMy(SpuSearchRequest request) {
        MemberInfo memberInfo = this.memberService.getMemberInfo();
        if (memberInfo == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "无效的用户");
        if (memberInfo.getGrade() == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "获取用户等级失败");

        final StoreInfo storeInfo = this.memberService.getStoreInfo();
        final List<GoodsSpuEx> goodsByCategory = this.goodsSpuDao.findMySpuListByKeyword(storeInfo.getId(), request.getKeyword(), request.toRowBounds());//分页信息
        for (GoodsSpuEx item : goodsByCategory) {
            //计算会员价,可赚
            item.setMemberPrice(this.getMemberPrice(memberInfo.getGrade(), item.getMinPrice(), item.getOriginPrice()));
            item.setEarnPrice(this.getEarnPrice(item.getOriginPrice(), item.getMemberPrice()));
        }
        final List<SpuListResponse> goodsSpuListResponse = BeanUtils.toList(goodsByCategory, SpuListResponse.class);//类型转换
        return PageableResponse.create(goodsByCategory, goodsSpuListResponse);
    }

    //获取 spu 详情,根据 spuId
    public SpuDetailResponse getSpuDetail(SpuDetailRequest request) {
        MemberInfo memberInfo = this.memberService.getMemberInfo();
        if (memberInfo == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "无效的用户");
        GoodsSpuEx spu = this.goodsSpuDao.findSpuById(request.getSpuId());
        if (spu == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "该商品已下架");
        //计算会员价,可赚
        spu.setMemberPrice(this.getMemberPrice(memberInfo.getGrade(), spu.getMinPrice(), spu.getOriginPrice()));
        spu.setEarnPrice(this.getEarnPrice(spu.getOriginPrice(), spu.getMemberPrice()));
        spu.setPointValue(this.pointService.computeGoodsPointValue(spu.getMemberPrice()));
        //查询banner
        final Example bannerExample = new Example(GoodsSpuBanner.class);
        bannerExample.createCriteria().andEqualTo(GoodsSpuBanner.SPU_ID, request.getSpuId());
        bannerExample.orderBy(GoodsSpuBanner.DISPLAY_ORDER);
        final List<GoodsSpuBanner> goodsSpuBanners = this.goodsSpuBannerDao.selectByExample(bannerExample);
        //查询图片详情
        final Example imageExample = new Example(GoodsSpuImage.class);
        imageExample.createCriteria().andEqualTo(GoodsSpuImage.SPU_ID, request.getSpuId());
        imageExample.orderBy(GoodsSpuImage.DISPLAY_ORDER);
        final List<GoodsSpuImage> goodsSpuImages = this.goodsSpuImageDao.selectByExample(imageExample);
        //查询规格
        final List<SpuDetailResponseSpec> specsBySpuId = this.goodsSpuSpecDao.findSpecsBySpuId(request.getSpuId());
        //返回
        SpuDetailResponse response = BeanUtils.toBean(spu, SpuDetailResponse.class);
        response.setIsStar(YNEnum.parse(this.storeService.isStar(spu.getId())));//是否收藏
        response.setBanners(BeanUtils.toList(goodsSpuBanners, SpuDetailResponseBanner.class));
        response.setImages(BeanUtils.toList(goodsSpuImages, SpuDetailResponseImage.class));
        response.setSpecs(specsBySpuId);
        return response;
    }

    //获取 spu 详情,公开
    public SpuDetailPublicResponse getSpuDetailPublic(SpuDetailRequest request) {
        GoodsSpuEx spu = this.goodsSpuDao.findSpuById(request.getSpuId());
        if (spu == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "该商品已下架");
        //查询banner
        final Example bannerExample = new Example(GoodsSpuBanner.class);
        bannerExample.createCriteria().andEqualTo(GoodsSpuBanner.SPU_ID, request.getSpuId());
        bannerExample.orderBy(GoodsSpuBanner.DISPLAY_ORDER);
        final List<GoodsSpuBanner> goodsSpuBanners = this.goodsSpuBannerDao.selectByExample(bannerExample);
        //查询图片详情
        final Example imageExample = new Example(GoodsSpuImage.class);
        imageExample.createCriteria().andEqualTo(GoodsSpuImage.SPU_ID, request.getSpuId());
        imageExample.orderBy(GoodsSpuImage.DISPLAY_ORDER);
        final List<GoodsSpuImage> goodsSpuImages = this.goodsSpuImageDao.selectByExample(imageExample);
        //查询规格
        final List<SpuDetailResponseSpec> specsBySpuId = this.goodsSpuSpecDao.findSpecsBySpuId(request.getSpuId());
        //返回
        SpuDetailPublicResponse response = BeanUtils.toBean(spu, SpuDetailPublicResponse.class);
        response.setBanners(BeanUtils.toList(goodsSpuBanners, SpuDetailResponseBanner.class));
        response.setImages(BeanUtils.toList(goodsSpuImages, SpuDetailResponseImage.class));
        response.setSpecs(specsBySpuId);
        return response;
    }

    //获取分享信息
    public SpuShareResponse getSpuShare(SpuShareRequest request) {
        final SpuShareResponse shareInfo = this.goodsSpuDao.findShareInfo(request.getSpuId());
        if (shareInfo == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "无效的商品");
        MemberInfo memberInfo = this.memberService.getMemberInfo();
        shareInfo.setNickName(memberInfo.getNickName());
        //替换宏
        if (StringUtils.contains(shareInfo.getShareText(), SPU_SHARE_MACRO_PRICE)) {
            BigDecimal originPrice = this.configService.roundPrice(shareInfo.getOriginPrice());
            final DecimalFormat decimalFormat = new DecimalFormat("0.00");
            String originPriceStr = decimalFormat.format(originPrice);
            String shareText = StringUtils.replace(shareInfo.getShareText(), SPU_SHARE_MACRO_PRICE, originPriceStr);
            shareInfo.setShareText(shareText);
        }

        //url 拼接,转为短 url
        String shareUrl = String.format("%s?spuId=%s", shareInfo.getShareUrl(), shareInfo.getSpuId());
        String tiny = this.tinyUrlService.urlToTiny(shareUrl);
        String tinyUrl = String.format("%s%s/%s", this.commonProperties.getBaseUrl(), TinyUrlController.URL, tiny);
        shareInfo.setShareUrl(tinyUrl);

        //查询 banner
        final Example bannerExample = new Example(GoodsSpuBanner.class);
        bannerExample.createCriteria().andEqualTo(GoodsSpuBanner.SPU_ID, request.getSpuId());
        bannerExample.orderBy(GoodsSpuBanner.DISPLAY_ORDER).asc();
        final List<GoodsSpuBanner> goodsSpuBanners = this.goodsSpuBannerDao.selectByExample(bannerExample);
        shareInfo.setBanners(BeanUtils.toList(goodsSpuBanners, SpuDetailResponseBanner.class));

        //查询 image
        final Example imageExample = new Example(GoodsSpuImage.class);
        imageExample.createCriteria().andEqualTo(GoodsSpuImage.SPU_ID, request.getSpuId());
        imageExample.orderBy(GoodsSpuImage.DISPLAY_ORDER).asc();
        final List<GoodsSpuImage> goodsSpuImages = this.goodsSpuImageDao.selectByExample(imageExample);
        shareInfo.setImages(BeanUtils.toList(goodsSpuImages, SpuDetailResponseImage.class));

        //返回
        return shareInfo;
    }

    //多商品分享
    public SpuMultiShareResponse getSpuMultiShare() {
        final String shareText = this.configService.getShareText();
        final SpuMultiShareResponse response = new SpuMultiShareResponse();
        response.setShareText(shareText);
        return response;
    }

    //获取 sku 详情,根据 spu 和 规格
    public SkuDetailResponse getSkuDetail(SkuDetailRequest request) {
        MemberInfo memberInfo = this.memberService.getMemberInfo();
        if (memberInfo == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "无效的用户");

        List<GoodsSkuWithSpecs> skuList = this.goodsSkuDao.findSkuWithSpecsListBySpu(request.getSpuId());
        final GoodsSkuWithSpecs goodsSkuWithSpecs = Linq.asEnumerable(skuList).firstOrDefault(sku -> KeyedCollectionUtils.equals(sku.getSpecs(), request.getSpecs()));
        if (goodsSkuWithSpecs == null)
            throw new BusinessException("01", "无对应 sku");
        //设置会员价,可赚
        goodsSkuWithSpecs.setMemberPrice(this.getMemberPrice(memberInfo.getGrade(), goodsSkuWithSpecs.getMinPrice(), goodsSkuWithSpecs.getOriginPrice()));
        goodsSkuWithSpecs.setEarnPrice(this.getEarnPrice(goodsSkuWithSpecs.getOriginPrice(), goodsSkuWithSpecs.getMemberPrice()));
        goodsSkuWithSpecs.setPointValue(this.pointService.computeGoodsPointValue(goodsSkuWithSpecs.getMemberPrice()));
        return BeanUtils.toBean(goodsSkuWithSpecs, SkuDetailResponse.class);
    }

    //根据 spu 获取唯一 sku
    public SkuDetailResponse getSkuDetailOnlyOne(SkuDetailRequest request) {
        MemberInfo memberInfo = this.memberService.getMemberInfo();
        if (memberInfo == null)
            throw new BusinessException(ErrorCode.ERROR_SERVER, "无效的用户");
        List<GoodsSkuEx> skuList = this.goodsSkuDao.findSkuListBySpu(request.getSpuId());
        if (skuList.size() != 1) {
            this.log.warn("spu 下的 sku 不唯一 count:" + skuList.size());
            throw new BusinessException(ErrorCode.ERROR_SERVER, "该商品配置错误");
        }
        //设置会员价,可赚
        final GoodsSkuEx goodsSkuWithSpecs = skuList.get(0);
        goodsSkuWithSpecs.setMemberPrice(this.getMemberPrice(memberInfo.getGrade(), goodsSkuWithSpecs.getMinPrice(), goodsSkuWithSpecs.getOriginPrice()));
        goodsSkuWithSpecs.setEarnPrice(this.getEarnPrice(goodsSkuWithSpecs.getOriginPrice(), goodsSkuWithSpecs.getMemberPrice()));
        goodsSkuWithSpecs.setPointValue(this.pointService.computeGoodsPointValue(goodsSkuWithSpecs.getMemberPrice()));
        return BeanUtils.toBean(goodsSkuWithSpecs, SkuDetailResponse.class);
    }

    //获取会员价
    public BigDecimal getMemberPrice(int memberGrade, BigDecimal minPrice, BigDecimal originPrice) {
        Assert.isTrue(memberGrade >= 0, "memberGrade must greater than or equals to 0");
        Assert.notNull(minPrice, "minPrice can not be null");
        Assert.isTrue(minPrice.compareTo(BigDecimal.ZERO) > 0, "minPrice must greater than 0");
        Assert.notNull(minPrice, "originPrice can not be null");
        Assert.isTrue(originPrice.compareTo(BigDecimal.ZERO) > 0, "originPrice must greater than 0");
        Assert.isTrue(originPrice.compareTo(minPrice) >= 0, "originPrice must greater than or equals to minPrice");

        memberGrade = Math.min(memberGrade, this.configService.getGradeLowerArray().length - 1);
        BigDecimal ratio = this.configService.getPriceRatioArray()[memberGrade];
        Assert.isTrue(ratio.compareTo(BigDecimal.ZERO) > 0, "ratio must greater than 0");
        MathContext mc = ConfigService.getMathContext();
        int n = this.configService.getGradeLowerArray().length;

        BigDecimal y_x = originPrice.subtract(minPrice, mc);
        BigDecimal y_x_n = y_x.divide(new BigDecimal(n), mc);
        BigDecimal y_x_n_r = y_x_n.multiply(ratio, mc);
        BigDecimal memberPrice = originPrice.subtract(y_x_n_r, mc);
        return this.configService.roundPrice(memberPrice);
    }

    //可赚
    public BigDecimal getEarnPrice(BigDecimal originPrice, BigDecimal memberPrice) {
        Assert.notNull(originPrice, "originPrice can not be null");
        Assert.notNull(memberPrice, "memberPrice can not be null");
        MathContext mc = ConfigService.getMathContext();
        BigDecimal earnPrice = originPrice.subtract(memberPrice, mc);
        return this.configService.roundPrice(earnPrice);
    }

    //获取收益率
    public BigDecimal getEarnRate(BigDecimal originPrice, BigDecimal memberPrice) {
        final BigDecimal earnPrice = this.getEarnPrice(originPrice, memberPrice);
        final BigDecimal earRate = earnPrice.divide(originPrice, ConfigService.getMathContext());
        return this.configService.roundEarnRate(earRate);
    }
}
